前面幾篇我們demo過 Opentelemetry 三本柱中的 trace 和 metric,今天我們來demo一下最後的 log 資料,讓整個觀測性更加完善。
除了之前的@opentelemetry/sdk-node
和@opentelemetry/auto-instrumentations-node
,我們還需要 @opentelemetry/exporter-logs-otlp-http
來導出日誌資料。
在 Node.js 應用中,傳統的日誌處理方式通常是透過一些常見的日誌庫來實現,如 Winston、Bunyan、Pino等等,它們都支持JSON格式的日誌,同時也將日誌分級。
而在 Opentelemetry SDK 使用函數getNodeAutoInstrumentations
時,也會自動引入相關的庫: @opentelemetry/instrumentation-bunyan
、@opentelemetry/instrumentation-pino
、@opentelemetry/instrumentation-winston
來覆蓋該日誌依賴的使用,使其與 OpenTelemetry 無縫集成。
本次demo,我們就先使用 Bunyan 這個日誌依賴:
const bunyan = require('bunyan');
const logger = bunyan.createLogger({
name: 'node-demo-service',
});
module.exports = {
logger,
};
接下來,除了呼叫getNodeAutoInstrumentations
來自動引入相關的庫,再來就是將 log exporter 寫入sdk:
const logExporter = new OTLPLogExporter({
url: 'http://localhost:4318/v1/logs', // 指向你的本地 OTLP Collector
});
const otelSdk = new NodeSDK({
logRecordProcessor: logExporter,
resource,
instrumentations: [getNodeAutoInstrumentations()],
});
otelSdk.start();
其中,http://localhost:4318/v1/logs
是本地 opentelemetry collector 的endpoint。
現在我們才聊到 opentelemetry collector,是因為之前的 tracing data 和 metric data 在opentelemtry sdk中、都可以直接 export 給相關的服務;但目前我看到的 log service 都沒辦法直接接收 opentelemetry 蒐集的 log data,所以必須透過 opentelemetry collecter 來整理和轉發 logs data。
我們透過docker compose 來運行本地的 opentelemetry collecter。首先,新增一個設定檔otel-collector-config.yaml
:
receivers:
otlp:
protocols:
grpc:
http:
endpoint: '0.0.0.0:4318'
exporters:
loki:
endpoint: 'http://self-loki:3100/loki/api/v1/push'
service:
pipelines:
logs:
receivers: [otlp]
exporters: [loki]
在這裡,我們使用 Loki 來作為日誌存儲服務,OpenTelemetry Collector 將接收日誌並轉發至 Loki。
而 docker-compose 中,設定 opentelemetry-collector:
self-otel-collector:
container_name: self-otel-collector
image: otel/opentelemetry-collector-contrib:latest
command: ['--config=/etc/otel-collector-config.yaml']
volumes:
- ./config/otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- '4317:4317' # OTLP gRPC 接口
- '4318:4318' # OTLP HTTP 接口
depends_on:
- self-loki
networks:
- otel-network
我們把 opentelemetry collector 的 HTTP endpoint定義為 http://localhost:4318
,要跟我們node sdk中設定的一致,才能傳送正確。
Loki 是由 Grafana Labs 開發的高擴展性日誌聚合系統,類似於 Prometheus 處理指標的方式,Loki 以最小的索引處理並查詢日誌數據。
我們要在本地運行它,也可以透過 docker-compose的方式,不過先定義下設定檔loki-local-config.yaml
:
auth_enabled: false
server:
http_listen_port: 3100
common:
instance_addr: 0.0.0.0
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
透過docker-compose 運行後,可以透過
curl -s "http://localhost:3100/loki/api/v1/label" | jq .
來查看Loki服務能否接收到logs,如果可以的話會回傳一個 success response:
{
"status": "success"
}
Grafana 是一個強大的可視化工具,可以接入不同的數據源、將數據可視化。我們今天就是利用 Grafana 來連接 Loki 服務、獲取並展示logs data。
grafana:
image: grafana/grafana:latest
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
ports:
- '3080:3000' # Grafana Web UI
depends_on:
- self-loki
networks:
- otel-network
新增 loki 數據源
在docker compose 中定義的 loki url
回到我們NodeJS應用,新增一個api來寫入log:
app.get('/demo', async (req, res) => {
logger.info('start /demo api');
await sleep(1500);
logger.warn('wair for a while');
await sleep(1800);
logger.warn('wair a little bit longer');
await sleep(2000);
logger.info('success');
res.json({ message: 'hello nodejs opentelemetry' });
});app.get('/demo', async (req, res) => {
logger.info('start /demo api');
await sleep(1500);
logger.warn('wair for a while');
await sleep(1800);
logger.warn('wair a little bit longer');
await sleep(2000);
logger.info('success');
res.json({ message: 'hello nodejs opentelemetry' });
});
呼叫該 api 後,我們可以在 Grafana 介面上查看結果
本次的 demo 展示了如何在 NodeJS 應用中設置 OpenTelemetry 日誌,並通過 OpenTelemetry Collector 將日誌導出到 Loki,最終使用 Grafana 來可視化日誌數據。
本次demo可以在此 Github repository上查看。